﻿namespace Moly.VB6
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using Build;
    using Com;
    using misc;

    public class Vb6Project : IProject
    {
        private int _buildError;
        private readonly VBPParser parser;
        public string Assembly { get; private set; }
        public long ChangeSet;
        public string BuildDir { get; set; }

        private void ValidateBuildDir()
        {
            if (!Directory.Exists(BuildDir))
                Directory.CreateDirectory(BuildDir);
        }

        private Vb6Project(VBPParser psr)
        {
            parser = psr;
            Assembly = psr.GetUnique("ExeName32").ToLower();

            if (string.IsNullOrEmpty(Assembly))
                throw new Exception(string.Format("Invalid output name '{0}' for {1}", Assembly, psr.File));

            LoadDependencies(psr);
            _buildError = -1;
            string f;
            ModificationDate = Utility.GetNewestDate(System.IO.Path.GetDirectoryName(psr.File), "*.*",out f, "vbp");
            
            var t = File.GetLastWriteTime(psr.File);
            ModificationDate = ModificationDate < t ? t : ModificationDate;
        }

        public bool IsApplication
        {
            get { return Assembly.EndsWith("exe"); }
        }

        public ModuleList Dependencies { get; private set; }

        public DateTime ModificationDate { get; private set; }

        public string Target
        {
            get
            {
                ValidateBuildDir();
                return System.IO.Path.Combine(BuildDir, Assembly);
            }
        }

        private void LoadDependencies(VBPParser psr)
        {
            Dependencies = new ModuleList();
            psr.Dependencies.ForEach(d => Dependencies.Add(ModuleID.FromVbpReferenceLine(d)));
            Dependencies.FindAll(d => d.Type == ModuleID.DepType.VBP).
                ForEach(d => d.AddVbpFullPath(Path.GetDirectoryName(psr.File)));
        }

        public string ProjectFile
        {
            get { return parser.File; }
        }

        private void updateFile(string refDll)
        {
            refDll = refDll ?? string.Empty;

            if (!isApplication)
            {
                //compatibility
                parser.UpdateUnique("CompatibleMode", (refDll != string.Empty) ? 2 : 0);
                parser.UpdateUnique("VersionCompatible32", string.Empty);
                parser.UpdateUnique("CompatibleEXE32", refDll);
            }

            /*/version details
            var v = version.Split('.');
            parser.UpdateUnique("MajorVer", v[0]);
            parser.UpdateUnique("MinorVer", v[1]);
            parser.UpdateUnique("RevisionVer", v[2]);
            //pdb
            parser.UpdateUnique("CodeViewDebugInfo", -1);
            */

            UpdateDependencies();
        }

        public void UpdateDependencies()
        {
            for (var i = 0; i < Dependencies.Count; i++)
                parser.UpdateDependency(i, Dependencies[i].ToString());
        }

        public string Dir
        {
            get { return System.IO.Path.GetDirectoryName(parser.File); }
        }

        public void Compile(string refDll)
        {
            var temp = Path.GetTempFileName();
            var pathCopy = Path.Combine(Path.GetDirectoryName(ProjectFile), "AutoGenerated.vbp");

            try
            {
                updateFile(refDll);
                if (BeforEdit != null)
                    BeforEdit(ProjectFile);


                parser.SaveCopy(pathCopy);

                if(AfterEdit != null)
                    AfterEdit(ProjectFile);

                Console.WriteLine("Compiling:{0}", Path.GetFileName(parser.File));
                executeCompile(pathCopy, temp);
                if(_buildError !=0)
                {
                    var error = File.ReadAllText(temp);
                    Console.Error.WriteLine("Build of {0} failed with error {1}", ProjectFile, error);
                    throw new Exception(error);
                }
            }
            finally
            {
                try
                {
                   // if (File.Exists(pathCopy))
                      //  File.Delete(pathCopy);

                    if (File.Exists(temp))
                        File.Delete(temp);
                }
                catch
                {
                    Console.WriteLine("[Warning] can't remove temp files {0} or {1}", temp, ProjectFile);
                }
            }
        }

        public event Action<string> BeforEdit;
        public event Action<string> AfterEdit;
        public string GetPath()
        {
            return ProjectFile;
        }

        private void executeCompile(string toCompile, string temp)
        {
            ValidateBuildDir();

            var psi = new ProcessStartInfo
            {
                FileName = Utility.GetVb6Path(),
                Arguments = string.Format("/make \"{0}\" /outdir {2} /out \"{1}\"", toCompile, temp, BuildDir),
                WorkingDirectory = System.IO.Path.GetDirectoryName(toCompile),
                UseShellExecute = false,
                CreateNoWindow = true
            };

            var process = Process.Start(psi);
            process.WaitForExit();
            _buildError = process.ExitCode;
        }

        private bool isApplication
        {
            get
            {
                return Assembly.EndsWith("exe");
            }
        }

        public List<string> SrcFiles
        {
            get { return parser.SrcFiles; }
        }

        public override string ToString()
        {
            return Assembly;
        }

        public void Save()
        {
            parser.SaveCopy(ProjectFile);
        }

        public void ValidateBuild()
        {
            if (_buildError != 0)
                throw new Exception("Project is not built successfully.");
        }


        public static IProject Create(string localPath)
        {
            if (!File.Exists(localPath))
                throw new Exception("Project File is missing:" + localPath);

            return new Vb6Project(new VBPParser(localPath));
        }
    }
}